#include "adaptor_log.h"
#include "adaptor_memory.h"
#include "pin_db.h"
#include "sys/types.h"
#include "tee_defines.h"
#include <pin_auth.h>
#include <pin_func.h>
#include <stdint.h>
#include <string.h>
#include <tee_crypto_api.h>
#include <tee_log.h>
#include <securec.h>

// This is a stub implementation, please implement the function when porting to hardware
int GetDevUdid(char *udid, uint32_t udidLen) {
  (void)udid;
  (void)udidLen;
  return 0;
}

PIN_AUTH_IMPL(EnrollPin) {
  TEE_Result ret;

  if (!check_param_type(param_type, TEE_PARAM_TYPE_MEMREF_INPUT,
                        TEE_PARAM_TYPE_MEMREF_OUTPUT,
                        TEE_PARAM_TYPE_MEMREF_OUTPUT, TEE_PARAM_TYPE_NONE)) {
    LOG_ERROR("Bad expected parameter types");
    return TEE_ERROR_BAD_PARAMETERS;
  }

  if (params[0].memref.buffer == NULL ||
      params[0].memref.size != sizeof(PinEnrollParam) ||
      params[1].memref.buffer == NULL ||
      params[1].memref.size != RESULT_TLV_LEN ||
      params[2].memref.buffer == NULL ||
      params[2].memref.size != sizeof(uint32_t)) {
    LOG_ERROR("Invalid parameters");
    return TEE_ERROR_BAD_PARAMETERS;
  }

  PinEnrollParam *pinEnrollParam = (PinEnrollParam *)params[0].memref.buffer;
  Buffer *retTlv = Malloc(sizeof(Buffer));
  retTlv->buf = params[1].memref.buffer;
  retTlv->maxSize = params[1].memref.size;
  retTlv->contentSize = 0;

  ret = DoEnrollPin(pinEnrollParam, retTlv);
  if (ret != TEE_SUCCESS) {
    LOG_ERROR("InvokeCommand Failed 0x%x.", ret);
    return ret;
  }

  /* return the contentSize of retTlvBuf */
  TEE_MemMove(params[2].memref.buffer, &retTlv->contentSize,
              sizeof(retTlv->contentSize));
  return TEE_SUCCESS;
}

PIN_AUTH_IMPL(GetExecutorInfo) {
  TEE_Result ret;

  if (!check_param_type(param_type, TEE_PARAM_TYPE_MEMREF_INOUT,
                        TEE_PARAM_TYPE_NONE, TEE_PARAM_TYPE_NONE,
                        TEE_PARAM_TYPE_NONE)) {
    LOG_ERROR("Bad expected parameter types");
    return TEE_ERROR_BAD_PARAMETERS;
  }

  if (params[0].memref.buffer == NULL ||
      params[0].memref.size != sizeof(PinExecutorInfo)) {
    LOG_ERROR("InvokeCommand with bad, cmd is");
    return TEE_ERROR_BAD_PARAMETERS;
  }

  PinExecutorInfo *pinExecutorInfo = (PinExecutorInfo *)params[0].memref.buffer;

  ret = DoGetExecutorInfo(pinExecutorInfo);
  return ret;
}

PIN_AUTH_IMPL(VerifyTemplateData) {
  TEE_Result ret;

  if (!check_param_type(param_type, TEE_PARAM_TYPE_MEMREF_INPUT,
                        TEE_PARAM_TYPE_VALUE_INPUT, TEE_PARAM_TYPE_NONE,
                        TEE_PARAM_TYPE_NONE)) {
    LOG_ERROR("Bad expected parameter types");
    return TEE_ERROR_BAD_PARAMETERS;
  }

  // The TemplateDataList can be empty
  if (params[0].memref.buffer == NULL && params[0].memref.size == 0 &&
      params[1].value.a == 0) {
    LOG_ERROR("InvokeCommand with empty list, which is allowed");
    ret = DoVerifyTemplateData(params[0].memref.buffer, params[1].value.a);
    if (ret != TEE_SUCCESS) {
      LOG_ERROR("InvokeCommand Failed 0x%x", ret);
    }
  } else if (params[0].memref.buffer == NULL ||
             params[0].memref.size != params[1].value.a * sizeof(uint64_t)) {
    return TEE_ERROR_BAD_PARAMETERS;
  }

  uint64_t *templateIdList = (uint64_t *)params[0].memref.buffer;
  uint32_t templateIdListLen = params[1].value.a;

  ret = DoVerifyTemplateData(templateIdList, templateIdListLen);
  if (ret != TEE_SUCCESS) {
    LOG_ERROR("InvokeCommand Failed 0x%x", ret);
  }

  return ret;
}

PIN_AUTH_IMPL(QueryPinInfo) {
  TEE_Result ret;
  if (!check_param_type(param_type, TEE_PARAM_TYPE_VALUE_INPUT,
                        TEE_PARAM_TYPE_MEMREF_OUTPUT, TEE_PARAM_TYPE_NONE,
                        TEE_PARAM_TYPE_NONE)) {
    LOG_ERROR("Bad expected parameter types");
    return TEE_ERROR_BAD_PARAMETERS;
  }
  if (params[1].memref.buffer == NULL ||
      params[1].memref.size != sizeof(PinCredentialInfos)) {
    LOG_ERROR("InvokeCommand with bad");
    return TEE_ERROR_BAD_PARAMETERS;
  }

  uint64_t templateId = (uint64_t)params[0].value.a << 32 | params[0].value.b;
  PinCredentialInfos *pinCredentialInfos =
      (PinCredentialInfos *)params[1].memref.buffer;

  ret = DoQueryPinInfo(templateId, pinCredentialInfos);

  return ret;
}

PIN_AUTH_IMPL(AuthPin) {
  TEE_Result ret;
  if (!check_param_type(
          param_type, TEE_PARAM_TYPE_MEMREF_INPUT, TEE_PARAM_TYPE_MEMREF_OUTPUT,
          TEE_PARAM_TYPE_VALUE_OUTPUT, TEE_PARAM_TYPE_VALUE_OUTPUT)) {
    LOG_ERROR("Bad expected parameter types");
    return TEE_ERROR_BAD_PARAMETERS;
  }
  if (params[0].memref.buffer == NULL ||
      params[0].memref.size != sizeof(PinAuthParam) ||
      params[1].memref.buffer == NULL ||
      params[1].memref.size != RESULT_TLV_LEN) {
    LOG_ERROR("InvokeCommand with bad");
    return TEE_ERROR_BAD_PARAMETERS;
  }

  PinAuthParam *pinAuthParam = (PinAuthParam *)params[0].memref.buffer;
  Buffer *retTlv = (Buffer *)Malloc(sizeof(Buffer));
  retTlv->buf = params[1].memref.buffer;
  retTlv->maxSize = params[1].memref.size;
  retTlv->contentSize = 0;
  uint32_t compareRet;

  ret = DoAuthPin(pinAuthParam, retTlv, &compareRet);

  params[2].value.a = ret;
  params[3].value.a = retTlv->contentSize;

  return ret;
}

PIN_AUTH_IMPL(WriteAntiBrute) {
  TEE_Result ret;
  if (!check_param_type(param_type, TEE_PARAM_TYPE_VALUE_INPUT,
                        TEE_PARAM_TYPE_NONE, TEE_PARAM_TYPE_NONE,
                        TEE_PARAM_TYPE_NONE)) {
    LOG_ERROR("Bad expected parameter types");
    return TEE_ERROR_BAD_PARAMETERS;
  }

  uint64_t templateId = (uint64_t)params[0].value.a << 32 | params[0].value.b;

  ret = DoWriteAntiBruteInfoToFile(templateId);
  return ret;
}

PIN_AUTH_IMPL(DeleteTemplate) {
  TEE_Result ret;

  if (!check_param_type(param_type, TEE_PARAM_TYPE_VALUE_INPUT,
                        TEE_PARAM_TYPE_NONE, TEE_PARAM_TYPE_NONE,
                        TEE_PARAM_TYPE_NONE)) {
    LOG_ERROR("Bad expected parameter types");
    return TEE_ERROR_BAD_PARAMETERS;
  }

  uint64_t templateId = (uint64_t)params[0].value.a << 32 | params[0].value.b;

  ret = DoDeleteTemplate(templateId);
  return ret;
}

PIN_AUTH_IMPL(GenerateAlgoParameter) {
  TEE_Result ret;
  if (!check_param_type(param_type, TEE_PARAM_TYPE_MEMREF_OUTPUT,
                        TEE_PARAM_TYPE_VALUE_OUTPUT,
                        TEE_PARAM_TYPE_VALUE_OUTPUT, TEE_PARAM_TYPE_NONE)) {
    LOG_ERROR("Bad expected parameter types");
    return TEE_ERROR_BAD_PARAMETERS;
  }

  if (params[0].memref.buffer == NULL ||
      params[0].memref.size != CONST_SALT_LEN * sizeof(uint8_t)) {
    LOG_ERROR("InvokeCommand with bad");
    return TEE_ERROR_BAD_PARAMETERS;
  }

#define DEVICE_UUID_LENGTH 65
  // This is a stub implementation, please implement the function when porting to hardware
  static const uint32_t deviceUuidLength = DEVICE_UUID_LENGTH;
  char localDeviceId[DEVICE_UUID_LENGTH] = {0};
  if (GetDevUdid(localDeviceId, deviceUuidLength) != 0) {
    LOG_ERROR("GetDevUdid failed");
    return GENERAL_ERROR;
  }

  uint8_t *algoParameter = (uint8_t *)params[0].memref.buffer;
  uint32_t algoParameterLength = CONST_SALT_LEN;
  uint32_t algoVersion;

  ret =
      DoGenerateAlgoParameter(algoParameter, &algoParameterLength, &algoVersion,
                              (uint8_t *)localDeviceId, deviceUuidLength);
  if (ret != TEE_SUCCESS) {
    LOG_ERROR("InvokeCommand Failed 0x%x", ret);
  }

  params[1].value.a = algoParameterLength;
  params[2].value.a = algoVersion;

  return ret;
}

PIN_AUTH_IMPL(GetAlgoParameter) {
  TEE_Result ret;
  if (!check_param_type(param_type, TEE_PARAM_TYPE_VALUE_INPUT,
                        TEE_PARAM_TYPE_MEMREF_OUTPUT,
                        TEE_PARAM_TYPE_VALUE_OUTPUT, TEE_PARAM_TYPE_NONE)) {
    LOG_ERROR("Bad expected parameter types");
    return TEE_ERROR_BAD_PARAMETERS;
  }

  if (params[1].memref.buffer == NULL ||
      params[1].memref.size != CONST_SALT_LEN * sizeof(uint8_t)) {
    LOG_ERROR("InvokeCommand with bad");
    return TEE_ERROR_BAD_PARAMETERS;
  }

  uint64_t templateId = (uint64_t)params[0].value.a << 32 | params[0].value.b;
  uint32_t saltLen = CONST_SALT_LEN;
  uint8_t *salt = (uint8_t *)params[1].memref.buffer;
  uint32_t algoVersion;

  ret = DoGetAlgoParameter(templateId, salt, &saltLen, &algoVersion);
  params[2].value.a = algoVersion;
  return ret;
}